package GP;

import java.util.ArrayList;
import java.util.Random;

import tree.Node;
import tree.Tree;

public class Mutaion {
	private Random random;
	final double mutationRate = 0.08;
	
	public Mutaion(Random random) {
		this.random = random;
	}
	
	
	public void aggressiveMutation(Tree tree){
		aggressiveMutation(tree.getHead(),tree);
	}

	private void aggressiveMutation(Node node,Tree tree) {	
		if (node!=null) {
			if (node.getParent()!=null && random.nextDouble() <  mutationRate) {
				//create random node from beginning (delete all his children)
				Node randomNode=node.getRandNodeWithTheSameReturnType(tree.getMaxDepth(),null);	
				//System.out.println("mutate to: "+randomNode);
				if (randomNode!=null){
					tree.replaceSubTree(node,randomNode);	
					node.setParent(null);
					tree.calcFitness();
				}
			}else{
				ArrayList<Node> sons = node.getSons();
				if (sons.size() >=1){
					//mutate first son
					aggressiveMutation(sons.get(0),tree);
				}
				if (sons.size()>=2){
					//mutate second son
					aggressiveMutation(sons.get(1),tree);	
				}				
			}
		}
	}
	
	/**
	 * make a soft mutation on a given tree
	 * @param tree given tree
	 */
	public void softMutation(Tree tree){
		ArrayList<Node> helpList = new ArrayList<Node>(); 
		for (Node n: tree.getAllNodesInTree()){
			helpList.add(n);
		}
		
		for (Node node: helpList){
			if (node.getParent()!=null && random.nextDouble() <  mutationRate) {
				Node randomNode = createSoftRandomNode(node,tree);
				if (randomNode!=null){
					tree.replaceNode(node,randomNode);
					//randomNode.setParent(node.getParent());
					//node.setParent(null);
				}
			}
		}
	}

	/*
	 *create random node with the same categorization and the same children
	 * @param node
	 * @return new Random Node
	 */
	private Node createSoftRandomNode(Node node,Tree t) {
		Node returnNode =  node.getRandNode(t.getMaxDepth(),null);	
		if (returnNode!=null){
			returnNode.setSons(node.getSons());
			for (Node son : node.getSons()){		
				son.setParent(returnNode);
			}
		}
		
		
		//returnNode.setParent(node.getParent());
		//node.setParent(null);
		return returnNode;
	}
	
}
